package Phase1;

import java.lang.reflect.*;
/**
 * Command information to request method from an object on a particular machine
 * @author byu.ryanpadilla
 *
 */
public class Command implements java.io.Serializable 
{

	private static final long serialVersionUID = 1L;
	
	private GlobalID objID;
	//private String className;
	private String methodName;
	private Class<?>[] paramTypes;
	private Object[] args;
	private boolean returnVal;
	/**
	 * null constructor
	 */
	public Command()
	{
		
	}
	/**
	 * Stores all the relevant information to invoke a method on an Object on some Machine.
	 * @param objID
	 * ID of object we want to find.  This contains the LocalID, Machine, and Port Number
	 * @param methodName
	 * The name of the method that will be called.
	 * @param paramTypes
	 * The type of each parameter in the method declaration.
	 * @param args
	 * The value of each parameter for the method that will be called. 
	 * @param returnVal
	 * True if the method has a return value, False for a void method.
	 */
	public Command(GlobalID objID, /*String className,*/ String methodName, 
					Class<?>[] paramTypes, Object[] args, boolean returnVal)
	{
	
		this.objID = objID;
		/*this.className = className;*/
		this.methodName = methodName;
		this.paramTypes = new Class[paramTypes.length];
		for(int i = 0; i < paramTypes.length; i++)
		{		
			this.paramTypes[i] = paramTypes[i];			
		}
		
		this.args = new Object[args.length];
		for(int i = 0; i < args.length; i++)
		{		
			this.args[i] = args[i];			
		}
		this.returnVal = returnVal;
	
	}
	/**
	 * Gets Object id (Machine ID, Port Number, Local Object ID)
	 * @return ObjectID, which contains the local object id, the Machine Number(ip and hostname), 
	 * and the port
	 */
	public GlobalID getObjID()
	{
	
		return this.objID;
	
	}
	/**
	 * Return boolean that determines if there is a return value for the invoked method.
	 * @return
	 * Return true if there is a return value, and false otherwise.
	 * 
	 */
	public boolean hasReturnValue()
	{
		return this.returnVal;
	}
	
	public String getMethodName()
	{
		return this.methodName;
	}

	/**
	 * This method executes the stored method on the object inputed as a parameter
	 * @param obj
	 * The object the stored method will be invoked on.
	 * @return Object returned by the invoked method.
	 */
	public Object invoke(Object obj) throws NoSuchMethodException, IllegalAccessException, 
										InvocationTargetException
	{	
		Method mymethod = obj.getClass().getMethod(this.methodName, this.paramTypes);
	    
		return mymethod.invoke(obj, this.args);
		
	}
	/**
	 * Concatenates the details of the command into a String
	 * @return String representation of the contained Command
	 */
	public String toString()
	{
		
		String result = this.objID.toString() + "\nMethod Called: " + this.methodName 
				+ "\nParamter Types: ";
		if(paramTypes.length > 0)
		{
			result += paramTypes[0].getName();
		}
		else
		{
			result += "None";
		}
		for(int i = 1; i < paramTypes.length; i++)
		{
			result += ", " + paramTypes[i].getName();
		}
		result += "\n";
		result += "Parameters: ";
		if(args.length > 0)
		{
			result += args[0];
		}
		else
		{
			result += "None";
		}
		for(int i = 1; i < args.length; i++)
		{
			result += ", " + args[i];
		}
		result += "\nReturn: " + this.returnVal;
		
		return result;
	}
/*	/**
	 * 
	 * A Test method - Prints if an object is a
	 * Primitive type, array, or an Object.
	 * 
	 * @param Object to be tested
	 * @return The Object passed in as a parameter
	 * 
	 */

/*	public Object warp(Object a)
	{
		if(a == null)
		{
			return null;
		}
		if(a.getClass().isPrimitive())
		{
			//not work, it goes to obj
			System.out.println("primitive");	
			return (Object) a;
		}
		if(a.getClass().isArray())
		{
			System.out.println("isarray");
			 
			return (Object) a;
		}
		if(a instanceof Object)
		{
			System.out.println("obj");
			return a;
		}
		
		
		Object t = a;
		return t;
	
	}
*/
}
